/*
 * main.cpp
 *
 * Copyright (c) 2012-2013 Baghdasaryan Andrew (relfock at gmail dot com)
 */
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>

#include <string>
#include <iostream>

#include <signal.h>

#include "serial.hpp"
#include "helper.hpp"
#include "udp_server.hpp"
#include "tcp_server.hpp"
#include "fingerprint.hpp"
#include "config_parser.hpp"

#define XML_CONFIG_NAME "config.xml"
#define USER_XML_CONFIG_NAME "user_access.xml"

/* system sleep */
SysSleep sys_sleep;

/* port-serial map */
std::map<std::string, Serial> port_map;

/* global_id objects map */
std::map<unsigned int, Base*> gids;

/* global_id per class map */
std::map<unsigned int, Base> vars;
std::map<unsigned int, Ping> pings;
std::map<unsigned int, Mode> modes;
std::map<unsigned int, Panel> panels;
std::map<unsigned int, Gsm> gsm_cmds;
std::map<unsigned int, Sensor> sensors;
std::map<unsigned int, Command> commands;
std::map<unsigned int, Timer> user_timers;
std::map<unsigned int, Fingerprint> fingerprint_cmds;
std::map<unsigned int, Function> functions;

std::map<int, User> users;

/* mutex for logging */
pthread_mutex_t logger_mutex;
xmlpp::DomParser xml_doc;
xmlpp::DomParser user_xml_doc;

/* mutex for saving config */
bool config_changed = false;
pthread_mutex_t save_config_mutex;

void signal_handler(int sig)
{
    pthread_mutex_lock(&save_config_mutex);
    logger(SM_LOG_INFO, "Smart Home server stopped"); 
    exit(0);
}

void* config_saver(void *arg)
{
    while(true) {
        sleep(5);
        pthread_mutex_lock(&save_config_mutex);
        if(config_changed){
            xml_doc.get_document()->write_to_file_formatted("config.xml");
            config_changed = false;
        }
        pthread_mutex_unlock(&save_config_mutex);
    }

    pthread_exit(NULL);
}

/* Main entry point of the Smart home daemon. */
int main(int argc, char **argv)
{   
    sys_sleep.gid = 0;
    sys_sleep.name = "sleep";
    gids[sys_sleep.gid] = &sys_sleep;
    pthread_t config_saver_thread;

    pthread_mutex_init(&logger_mutex, NULL);
    pthread_mutex_init(&save_config_mutex, NULL); 

    /* Register signal handlers. */
    signal(SIGTERM, &signal_handler);
    signal(SIGINT, &signal_handler);

    /*
     * Fork the process and have the parent exit. If the process was started
     * from a shell, this returns control to the user. Forking a new process is
     * also a prerequisite for the subsequent call to setsid().
     */
    if (pid_t pid = fork()) {
        if (pid > 0) {
            exit(0);
        }
        else {
            printf("First fork failed: %m");
            return 1;
        }
    }

    setsid();
    //chdir("/");
    umask(0);

    /* A second fork ensures the process cannot acquire a controlling terminal. */
    if (pid_t pid = fork()) {
        if (pid > 0) {
            exit(0);
        }
        else {
            printf("Second fork failed: %m");
            exit(0);
        }
    }

    /*
     * Close the standard streams. This decouples the daemon from the terminal
     * that started it.
     */
    close(0);
    close(1);
    close(2);

    /* We don't want the daemon to have any standard input. */
    if (open("/dev/null", O_RDONLY) < 0) {
        return 1;
    }

    /* Send standard output to a log file. */
    const char* output = "/var/log/sh/smart_home";
    const int flags = O_WRONLY | O_CREAT | O_APPEND;
    const mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
    if (open(output, flags, mode) < 0) {
        return 2;
    }

    /* Also send standard error to the same log file. */
    if (dup(1) < 0) {
        logger(SM_LOG_ERR, "Unable to dup output descriptor");
        return 1;
    }

    /* Parsing config.xml configuration file. */
    parse_config(XML_CONFIG_NAME);

    /* Parsing user_access.xml user configuration file. */
    parse_user_config(USER_XML_CONFIG_NAME);

    init_and_start_core();

    /* Initialize UDP/TCP servers on 2323/2324 ports. */
    init_udp_server();
    logger(SM_LOG_INFO,"Successfully initialized UDP server on port 2323.");

    init_tcp_server();
void init_tcp_server_non_ssl();
    init_tcp_server_non_ssl();
    logger(SM_LOG_INFO,"Successfully initialized TCP server on port 2324.");

    logger(SM_LOG_INFO, "Smart Home server started");

    /* start config saver thread */
    pthread_create(&config_saver_thread, NULL, &config_saver, NULL);

    /* exiting from the main thread. */
    while(true) {
        sleep(200);
    }
}
